package com.vueapi.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vueapi.domain.HjArticle;
import com.vueapi.domain.HjArticleTag;
import com.vueapi.domain.HjCategory;
import com.vueapi.domain.HjTag;
import com.vueapi.domain.dto.ArticleDTO;
import com.vueapi.domain.vo.ArticleVo;
import com.vueapi.domain.vo.BlogViewCountVO;
import com.vueapi.mapper.HjArticleMapper;
import com.vueapi.service.HjArticleService;
import com.vueapi.service.HjArticleTagService;
import com.vueapi.service.HjCategoryService;
import com.vueapi.service.HjTagService;
import com.vueapi.utils.RedisUtils;
import com.vueapi.utils.ResultUtils;
import com.vueapi.utils.TimeAgoUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @author 15442
 * @description 针对表【hj_article】的数据库操作Service实现
 * @createDate 2022-09-20 11:44:51
 */
@Service
public class HjArticleServiceImpl extends ServiceImpl<HjArticleMapper, HjArticle>
        implements HjArticleService {

    @Autowired
    @Lazy
    private HjCategoryService categoryService;

    @Resource
    private HjTagService tagService;

    @Resource
    private HjArticleTagService articleTagService;

    @Resource
    private RedisUtils redisUtils;

    @Qualifier("threadPoolTaskExecutor")
    @Autowired
    private ThreadPoolTaskExecutor executor;

    @Transactional
    @Override
    public ResultUtils addBlog(ArticleDTO articleDto) {

        // 创建 Article 对象，将数据进行拷贝
        HjArticle article = new HjArticle();
        BeanUtils.copyProperties(articleDto, article);

        // 字数统计
        int fontCounts = article.getContent().length();
        article.setFontCounts(fontCounts);

        // 预计阅读时间计算
        article.setReadTime((int) Math.round((double) (fontCounts / 300)));

        // 根据博客分类ID查询博客分类名称，并存入
        HjCategory category = categoryService.getById(article.getCategoryId());
        article.setCategoryName(category.getName());

        // 插入数据库
        save(article);

        // 取出所有的标签找到对应的ID
        List<String> tags = articleDto.getTags();
        LambdaQueryWrapper<HjTag> wrapper = new LambdaQueryWrapper<HjTag>().select(HjTag::getId).in(HjTag::getName, tags);
        List<HjTag> list = tagService.list(wrapper);

        // 创建 标签与博客 的对应关系，存入表中
        ArrayList<HjArticleTag> articleTagList = new ArrayList<>();
        for (HjTag hjTag : list) {
            HjArticleTag articleTag = new HjArticleTag();
            articleTag.setTagId(hjTag.getId());
            articleTag.setArticleId(article.getId());

            articleTagList.add(articleTag);
        }
        articleTagService.saveBatch(articleTagList);


        // 修改分类的关联文章数量
        categoryService.updateArticleCount(category.getId());

        // 修改标签关联数量
        for (HjTag hjTag : list) {
            tagService.updateArticleCount(hjTag.getId());
        }

        return ResultUtils.success("发布成功", null);
    }

    @Override
    public ResultUtils listPage(Integer pageNum, Integer pageSize, String title, Integer categoryId, Integer isPublish) {
        // 分页查询
        Page<HjArticle> articlePage = page(new Page<>(pageNum, pageSize), new LambdaQueryWrapper<HjArticle>()
                .like(StringUtils.isNotBlank(title), HjArticle::getTitle, title)
                .like(categoryId != null, HjArticle::getCategoryId, categoryId)
                .like(isPublish != null, HjArticle::getIsPublish, isPublish)
                .orderByDesc(HjArticle::getIsWeight));

        // 获取结果集
        List<HjArticle> records = articlePage.getRecords();

        // 循环写入 VO
        List<ArticleVo> articleVos = new ArrayList<>();
        for (HjArticle article : records) {
            // 根据文章ID获取该文章的所有标签
            List<String> tags = articleTagService.listByArticle(article.getId());

            ArticleVo articleVo = new ArticleVo();
            BeanUtils.copyProperties(article, articleVo);

            // 将标签塞进文章VO对象，存入集合
            articleVo.setTags(tags);
            articleVos.add(articleVo);
        }

        // 创建新的 page 对象，以便于塞入 VO数组
        Page<ArticleVo> articleVoPage = new Page<>();
        BeanUtils.copyProperties(articlePage, articleVoPage);

        // 设置VO数组
        articleVoPage.setRecords(articleVos);
        return ResultUtils.success(articleVoPage);
    }

    @Override
    public ResultUtils top(Integer id) {
        HjArticle article = getById(id);
        article.setIsWeight(article.getIsWeight() == 0 ? 1 : 0);
        updateById(article);
        return ResultUtils.success("置顶成功!", null);
    }

    @Override
    public ResultUtils changePublish(Integer id) {
        HjArticle article = getById(id);
        article.setIsPublish(article.getIsPublish() == 0 ? 1 : 0);
        updateById(article);
        return ResultUtils.success();
    }

    @Override
    public ResultUtils changeComment(Integer id) {
        HjArticle article = getById(id);
        article.setIsComment(article.getIsComment() == 0 ? 1 : 0);
        updateById(article);
        return ResultUtils.success();
    }

    @Override
    public ResultUtils getArticleById(Integer id) {

        // 将数据拷贝为 VO
        HjArticle article = getById(id);
        if (article == null) {
            return ResultUtils.fail("未查找到文章！");
        }
        ArticleVo articleVo = new ArticleVo();
        BeanUtils.copyProperties(article, articleVo);

        // 根据ID查找所有标签
        List<String> tags = articleTagService.listByArticle(id);

        // 将 tags 存入
        articleVo.setTags(tags);

        // 异步更新浏览量
        CompletableFuture.runAsync(() -> {
            redisUtils.addArticleViewCountByRedis(article.getId());
        }, executor);

        // 返回数据
        return ResultUtils.success(articleVo);
    }

    @Transactional

    @Override
    public ResultUtils updateBlog(ArticleDTO articleDTO) {

        // 创建 Article 对象，将数据进行拷贝
        HjArticle article = new HjArticle();
        BeanUtils.copyProperties(articleDTO, article);

        // 字数统计
        int fontCounts = article.getContent().length();
        article.setFontCounts(fontCounts);

        // 预计阅读时间计算
        article.setReadTime((int) Math.round((double) (fontCounts / 300)));

        // 根据博客分类ID查询博客分类名称，并存入
        HjCategory category = categoryService.getById(article.getCategoryId());
        article.setCategoryName(category.getName());

        // 更新
        updateById(article);

        // 取出所有的标签找到对应的ID
        List<String> tags = articleDTO.getTags();
        LambdaQueryWrapper<HjTag> wrapper = new LambdaQueryWrapper<HjTag>().select(HjTag::getId).in(HjTag::getName, tags);
        List<HjTag> list = tagService.list(wrapper);

        // 创建 标签与博客 的对应关系，存入表中
        ArrayList<HjArticleTag> articleTagList = new ArrayList<>();
        for (HjTag hjTag : list) {
            HjArticleTag articleTag = new HjArticleTag();
            articleTag.setTagId(hjTag.getId());
            articleTag.setArticleId(article.getId());
            articleTagList.add(articleTag);
        }
        articleTagService.remove(new LambdaQueryWrapper<HjArticleTag>().eq(HjArticleTag::getArticleId, article.getId()));
        articleTagService.saveBatch(articleTagList);


        // 修改分类的关联文章数量
        categoryService.updateArticleCount(category.getId());

        // 修改标签关联数量
        for (HjTag hjTag : list) {
            tagService.updateArticleCount(hjTag.getId());
        }

        return ResultUtils.success("修改成功", null);
    }

    @Transactional
    @Override
    public ResultUtils deleteBlogs(String ids) {

        List<String> idsList = Arrays.asList(ids.split(","));
        List<HjArticle> articles = listByIds(idsList);

        for (HjArticle article : articles) {
            // 删除标签与文章的关联关系
            articleTagService.remove(new LambdaQueryWrapper<HjArticleTag>().eq(HjArticleTag::getArticleId, article.getId()));

            // 修改分类的关联文章数量
            categoryService.updateArticleCount(article.getCategoryId());

            List<HjTag> tagList = articleTagService.listByArticleIdAllColumn(article.getId());

            // 修改标签关联数量
            for (HjTag hjTag : tagList) {
                tagService.updateArticleCount(hjTag.getId());
            }
        }

        return ResultUtils.success("删除成功", null);
    }

    @Override
    public List<BlogViewCountVO> getHotBlog() {
        // 先查询访问量最多的前六个文章
        // 然后再用 stream 流封装为 VO 返回
        return list(new LambdaQueryWrapper<HjArticle>()
                .select(HjArticle::getTitle, HjArticle::getViewCounts)
                .orderByDesc(HjArticle::getViewCounts)
                .last("limit 5"))
                .stream()
                .map(a -> new BlogViewCountVO(a.getTitle(), a.getViewCounts()))
                .collect(Collectors.toList());
    }

    @Override
    public Integer getCount() {
        return this.baseMapper.getCount();
    }

    @Override
    public List<Integer> getBlogAddCount() {
        ArrayList<Integer> dataCountVOS = new ArrayList<>();
        for (int i = 7; i >= 1; i--) {

            Date today = TimeAgoUtils.getTodayZeroTimePre(i);
            Date lastDay = null;
            // 0代表了今天，那么就要算今天到明天，所以要调用往后加天数的方法 暂时取消
            lastDay = TimeAgoUtils.getTodayZeroTimePre(i - 1);

            LambdaQueryWrapper<HjArticle> between = new LambdaQueryWrapper<HjArticle>().select(HjArticle::getId).between(HjArticle::getCreateTime, today, lastDay);
            dataCountVOS.add((int) count(between));
        }
        return dataCountVOS;
    }

    @Override
    public ResultUtils listPage(Integer pageNum, Integer pageSize, String title, Integer categoryId) {

        // 增加访问量，异步
        CompletableFuture.runAsync(() -> {
            redisUtils.addViewCountByRedis();
        }, executor);

        Page<HjArticle> articlePage = page(new Page<>(pageNum, pageSize), new LambdaQueryWrapper<HjArticle>()
                .like(StringUtils.isNotBlank(title), HjArticle::getTitle, title)
                .eq(HjArticle::getIsPublish, 1)
                .eq(categoryId != null, HjArticle::getCategoryId, categoryId)
                .orderByDesc(HjArticle::getIsWeight)
                .orderByDesc(HjArticle::getCreateTime));

        // 获取结果集
        List<HjArticle> records = articlePage.getRecords();

        // 循环写入 VO
        List<ArticleVo> articleVos = new ArrayList<>();
        for (HjArticle article : records) {
            // 根据文章ID获取该文章的所有标签
            List<String> tags = articleTagService.listByArticle(article.getId());

            ArticleVo articleVo = new ArticleVo();
            BeanUtils.copyProperties(article, articleVo);

            // 将标签塞进文章VO对象，存入集合
            articleVo.setTags(tags);
            articleVos.add(articleVo);
        }

        // 创建新的 page 对象，以便于塞入 VO数组
        Page<ArticleVo> articleVoPage = new Page<>();
        BeanUtils.copyProperties(articlePage, articleVoPage);

        // 设置VO数组
        articleVoPage.setRecords(articleVos);
        return ResultUtils.success(articleVoPage);
    }

    @Override
    public ResultUtils getArticleByCategoryId(Integer pageNum, Integer pageSize, Integer categoryId) {
        // 分页查询
        Page<HjArticle> articlePage = page(new Page<>(pageNum, pageSize), new LambdaQueryWrapper<HjArticle>()
                .eq(categoryId != null, HjArticle::getCategoryId, categoryId)
                .orderByDesc(HjArticle::getIsWeight));

        // 获取结果集
        List<HjArticle> records = articlePage.getRecords();

        // 循环写入 VO
        List<ArticleVo> articleVos = new ArrayList<>();
        for (HjArticle article : records) {
            // 根据文章ID获取该文章的所有标签
            List<String> tags = articleTagService.listByArticle(article.getId());

            ArticleVo articleVo = new ArticleVo();
            BeanUtils.copyProperties(article, articleVo);

            // 将标签塞进文章VO对象，存入集合
            articleVo.setTags(tags);
            articleVos.add(articleVo);
        }

        // 创建新的 page 对象，以便于塞入 VO数组
        Page<ArticleVo> articleVoPage = new Page<>();
        BeanUtils.copyProperties(articlePage, articleVoPage);

        // 设置VO数组
        articleVoPage.setRecords(articleVos);
        return ResultUtils.success(articleVoPage);
    }

    @Override
    public ResultUtils getArticleByTagId(Integer pageNum, Integer pageSize, Integer tagId) {

        // 根据 tagId 查找所有关联的 ArticleId
        List<HjArticleTag> list = articleTagService.list(new LambdaQueryWrapper<HjArticleTag>().eq(HjArticleTag::getTagId, tagId).select(HjArticleTag::getArticleId));

        List<Integer> ids = list.stream().map(HjArticleTag::getArticleId).collect(Collectors.toList());

        if (ids.size() <= 0) {
            return ResultUtils.success(new Page<HjArticle>());
        }

        // 分页查询
        Page<HjArticle> articlePage = page(new Page<>(pageNum, pageSize), new LambdaQueryWrapper<HjArticle>()
                .in(HjArticle::getId, ids));

        // 获取结果集
        List<HjArticle> records = articlePage.getRecords();

        // 循环写入 VO
        List<ArticleVo> articleVos = new ArrayList<>();
        for (HjArticle article : records) {
            // 根据文章ID获取该文章的所有标签
            List<String> tags = articleTagService.listByArticle(article.getId());

            ArticleVo articleVo = new ArticleVo();
            BeanUtils.copyProperties(article, articleVo);

            // 将标签塞进文章VO对象，存入集合
            articleVo.setTags(tags);
            articleVos.add(articleVo);
        }

        // 创建新的 page 对象，以便于塞入 VO数组
        Page<ArticleVo> articleVoPage = new Page<>();
        BeanUtils.copyProperties(articlePage, articleVoPage);

        // 设置VO数组
        articleVoPage.setRecords(articleVos);
        return ResultUtils.success(articleVoPage);
    }

    @Override
    public ResultUtils getNewsArticle() {
        LambdaQueryWrapper<HjArticle> wrapper = new LambdaQueryWrapper<HjArticle>().orderByDesc(HjArticle::getCreateTime).last("limit 5");
        List<HjArticle> hjArticles = list(wrapper);
        return ResultUtils.success(hjArticles);
    }

    @Override
    public List<String> getAllUseCategoryByIds(List<Integer> categoryIds) {
        return baseMapper.getAllUseCategoryByIds(categoryIds);
    }
}




